#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>

#define SIZE 100
#define NUM 100

//补充实现重定向，主要是识别问题，可能有一下情况
//ls -a -l > log.txt 后面很多空格
//ls -a -l>log.txt 
//ls -a -l >> log.txt
//ls -a -l
//cat < log.txt
//cat log.txt
//也就是考虑有无空格问题，三种重定向问题，主要思想：命令是识别前面的，文件是后面的，把重定向的符号修改为\0,那么strtok就能直接识别前面的，再单独去除后面的文件即可

char buffer[NUM];
char* argv[SIZE];
char command_line[NUM];

#define INPUT 0x0
#define OUTPUT 0x1
#define APPEND 0x2
#define NONE  0x3
char newfiles[NUM];

//写个宏替换函数,仅仅是linux才有的do和while(0)
#define DES_SPACE(s) do{ while(command_line[s]==' ') s++; } while(0)

int main()
{
  while(1)
  {
  //1.首先输出系统及目录名称
    printf("[%s@%s %s]# ",getenv("USER"),getenv("HOSTNAME"),"myshell");
    fflush(stdout);
  
  //2.输入,每次commend_line都会重新清空  
    memset(command_line,'\0',sizeof(char)*NUM);
    fgets(command_line,NUM,stdin);
    //由于输入会包含回车，导致多了一个字符
    command_line[strlen(command_line)-1] = '\0'; 
  
  //2.2识别分离重定向符号,[  )
    int start=0,end=start+strlen(command_line);
    int flags = INPUT;
    while(start<end)
    {
      if(command_line[start]=='>')
      {
        //输入重定向
        flags = OUTPUT;
        command_line[start] = '\0';
        start++;
        if(command_line[start]=='>')
        {
          //追加重定向
          flags = APPEND;
          command_line[start] = '\0';
          start++;
        }
        //处理连续空格问题，前面命令后的空格在strtok中会去除掉，因为其输入的就是空格为分隔符
        DES_SPACE(start);
        break;
      }
      else if(command_line[start]=='<')
      {
        //输入重定向
        flags =INPUT;
        command_line[start] = '\0';
        start++;
        DES_SPACE(start);
        break;
      }
      else 
      {
        start++;
      }
    }
    

  //3.识别分离字符串
    argv[0] = strtok(command_line," ");
    int i = 1;
    if(strcmp(argv[0],"ls")==0)
    {
      //给ls添加一个颜色选项 --color=auto
      argv[i++]="--color=auto";
    }
    while(argv[i++]=strtok(NULL," "));//最后的NULL也会被弄进去，刚好符合结束符
    
    //测试
    //int j = 0;
    //for(j;j<i;j++)
    //{
    //  printf("%d %s\n",j,argv[j]);
    //}
    
  //4.TODO
  //实现内建命令，也就是调用内部函数而已
  
    if(strcmp(argv[0],"cd")==0&&argv[1]!=NULL)//注意cd改变的是父进程的路径，不需要创建子进程，直接更改父进程路径
    {
        chdir(argv[1]);
        continue;
    }
    
    if(strcmp(argv[0],"export")==0&&argv[1]!=NULL)//注意cd改变的是父进程的路径，不需要创建子进程，直接更改父进程路径
    {
        putenv(argv[1]);
        strcpy(buffer,argv[1]);//若有多个就用二维数组，这里只是举例说明需要临时保存而已，然后下面每次加载时循环putenv进去
        continue;
    }
    //这里只有一个举例，就直接put
    if(strlen(buffer))
      putenv(buffer);
    
    

  //5.进程创建，终止，等待，替换
    pid_t id = fork();
    if(id==0)
    {
      int fd;
      //重定向，根据前面所获取的哪种重定向信息，dup2更改fd，注意是在子进程内的，父进程依旧是shell
      switch(flags)
      {
        case INPUT:
          fd = open (command_line+start,O_RDONLY,0666);
          dup2(fd,0);
          break;
        case OUTPUT:
          fd = open(command_line+start,O_WRONLY|O_CREAT|O_TRUNC,0666);
          dup2(fd,1);
          break;
        case APPEND:
          fd = open(command_line+start,O_WRONLY|O_CREAT|O_APPEND,0666);
          dup2(fd,1);
          break;
        case NONE:      //默认NONE重定向，不变继续执行即可
                 break;
      }
      

      //chlid,进行进程替换
      execvp(argv[0],argv); 
      exit(-1);
    }
    int status = 0;
    int ret = waitpid(id,&status,0);
    if(WIFEXITED(status))//正常终止信号是0，为真
    {
      printf("执行及等待成功，退出码为%d\n",WEXITSTATUS(status));
      //printf("退出码%d，退出信号%d\n",(status>>8)&0xFF,status&0x7F);
    }
    else
    {
      printf("等待出错\n");
    }
  }
  return 0;
}
